package com.theta.gateway.infra.routecfg.service.impl;

import java.util.List;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Repository;

import com.google.common.collect.Lists;
import com.theta.gateway.infra.common.utils.JsonUtil;
import com.theta.gateway.infra.routecfg.model.converter.RouteDTOConverter;
import com.theta.gateway.infra.routecfg.model.dataobject.response.RouteInfoDO;
import com.theta.gateway.infra.routecfg.service.GatewayRouteRepository;

import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;


/**
 * gateway路由操作类
 * @author theta
 *
 */
@Slf4j
@Repository
public class GatewayRouteRepositoryImpl implements ApplicationEventPublisherAware,GatewayRouteRepository {

	@Autowired
	private RouteDefinitionWriter routeDefinitionWriter;
	@Autowired
	private RouteDefinitionRepository routeDefinitionRepository;
	@Autowired
	private RouteLocator routeLocator;
	
	private ApplicationEventPublisher eventPublisher;
	
	@Override
	public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
		this.eventPublisher=applicationEventPublisher;

	}

	
	@Override
	public void addRoute(RouteInfoDO dto,boolean refresh) {
		RouteDefinition routeDef= RouteDTOConverter.convertDto2RouteDefinition(dto);
//		routeDef.setFilters(Lists.newArrayList());
		this.routeDefinitionWriter.save(Mono.just(routeDef)).subscribe();
//		this.routeDefinitionRepository.save(route)
		if(refresh)
			this.eventPublisher.publishEvent(new RefreshRoutesEvent(this));
		
	}
	
	

	@Override
	public void addRoutes(List<RouteInfoDO> dtoList) {
		if(CollectionUtils.isNotEmpty(dtoList)) {
			dtoList.stream().forEach(dto->{
				RouteDefinition routeDef= RouteDTOConverter.convertDto2RouteDefinition(dto);
				this.routeDefinitionWriter.save(Mono.just(routeDef)).subscribe();
			});
			this.eventPublisher.publishEvent(new RefreshRoutesEvent(this));
		}else {
			log.warn("addRoutes fail .dtoList is null");
		}
		
		
	}


	@Override
	public void updateRoute(RouteInfoDO dto) {
		log.info("updateRoute===>{}",JsonUtil.toJsonString(dto));
		 this.delete(dto.getRouteId(),true);
		 try {
			this.addRoute(dto,true);
		} catch (Exception e) {
			log.error("",e);
			throw e;
		}
		
	}

	@Override
	public void delete(String id,boolean refresh) {
		log.info("start delete routeId:{}",id);
		this.routeDefinitionWriter.delete(Mono.just(id))
			.subscribe();
		if(refresh)
			this.eventPublisher.publishEvent(new RefreshRoutesEvent(this));
	  
	}
	
	

	@Override
	public void deleteBatch(List<String> ids,boolean refresh) {
		if(CollectionUtils.isNotEmpty(ids)) {
//			log.warn("deleteBatch routeIds {}",ids);
			ids.stream().forEach(id->{
//				this.routeDefinitionRepository.delete(Mono.just(id));
				this.delete(id, false);
			});
			if(refresh)
				this.eventPublisher.publishEvent(new RefreshRoutesEvent(this));
		}else {
			log.warn("deleteBatch routeIds is null");
		}
	}


	@Override
	public List<RouteInfoDO> getAllRoute() {
		Flux<RouteDefinition> routeDefs= this.routeDefinitionRepository.getRouteDefinitions();
		List<RouteInfoDO> routeRecords=Lists.newArrayList();
		routeDefs.collectList().subscribe(
				list->routeRecords.addAll(RouteDTOConverter.convertRouteDefinition2DTOBatch(list))
				);
//		log.info("routeLocator doOnNext---->{}",JsonUtil.toJsonString(list));
//		list.stream().forEach(r->log.info("id:{},uri:{},predicate:{}",r.getId(),r.getUri(),r.getPredicate()))
//		this.routeLocator.getRoutes().collectList()
//		.doOnNext(list->).subscribe();
		
		return routeRecords;
	}
	
	

}
